DATABASE_URL, read it, and make one
protected write.
Your database is the system of record — Ablo never hosts your data. Ablo is
the transaction layer on top of it: the client registers your Postgres
connection once, every write commits there behind row-level security, and the
confirmed rows fan out live to every connected client.
The schema is the API. Before you can read or commit anything — over the
client SDK, the HTTP API, or an agent — you must define a schema and push it
(
ablo push). Your models become the API surface: a task model is what makes
/v1/models/task exist. An API key authenticates you; the schema defines what
you can call. With no pushed schema there is nothing to transact against, and
your generated API Reference reflects your pushed models.1. Install
2. Declare a Schema
schema. Treat it like Prisma’s schema file:
it is the source of truth for typed model resources, realtime subscriptions, and
agent writes.
databaseUrl is server-only (the SDK throws if it sees one in a browser) and is
sent once over TLS to register the connection — it is stored sealed and never
echoed back. Use a dedicated non-superuser role: Ablo enforces tenant
isolation with row-level security and rejects superuser or BYPASSRLS roles.
3. Push Your Schema
Declaring the schema in your code is not enough — the server holds its own copy. The sync server imports no app schema; it learns your models only from what you push, per tenant. Until you push, the server rejects every write to a model it hasn’t seen:4. Provision Your Tables
Don’t want a connection string to leave your infrastructure? Keep
DATABASE_URL in your app only and expose one signed Data Source endpoint
instead — npx ablo init --storage endpoint scaffolds it from an ORM adapter
(prismaDataSource / drizzleDataSource), and you omit databaseUrl from
Ablo(...). Same product, same writes, same truth: your database is the system
of record. The full shape is in Connect Your Database.5. See it instantly
Once your schema is pushed and your tables exist,ablo init adds a small agent
at ablo/agent.ts. Run it:
6. Read and Update
7. Multiplayer and Coordination
There is no separate multiplayer mode. Use the same schema client for human UI, server actions, and agents; Ablo fans out confirmed writes and coordinates work on the same row. When a human or agent needs exclusive, ordered access to a row, take a claim. A claim is a disposable handle: it gives you the freshest row offclaim.data,
and auto-releases when it leaves scope (await using). Anyone else who claims
the same row queues behind you.
claim namespace exposes the rest of the coordination plane — each member
takes an options object:
ablo.tasks.claim.state({ id })— who currently holds the row, and the queue.ablo.tasks.claim.queue({ id })— the ordered list of waiters.ablo.tasks.claim.release({ id })— release a claim explicitly.ablo.tasks.claim.reorder({ id, order })— reprioritize the queue.
8. Next Steps
Keep using the schema client for app and agent writes. Reach for the advanced schema-less agent wrapper only when a worker intentionally cannot import the app schema.- CLI covers
ablo init, schema push, and migrations. - Integration Guide explains the full app, React, multiplayer, and agent path.
- Guarantees explains what confirmed writes and stale checks mean.
- Client Behavior covers errors, retries, and public imports.
- Connect Your Database covers both connection shapes —
databaseUrland the signed Data Source endpoint. - AI SDK Tool shows the same write path inside a tool call.
packages/sync-engine/examples/data-sourcecontains the runnable signed Data Source contract demo.